<?php
declare(strict_types=1);
namespace Service\VenderClient\PayCenterApi;
use Service\Code\Normal;
use Service\BaseService;
use Service\ExtraService\TaskWorker;

/**
 * 通联支付
 * @author Administrator
 */
class AllinpayApiClient extends BaseService
{
    protected $config;
    protected $url = 'https://vsp.allinpay.com/apiweb/unitorder/pay';
    protected $router = [
        'orderPay'      => 'https://vsp.allinpay.com/apiweb/unitorder/pay',
        'scanPay'       => 'https://vsp.allinpay.com/apiweb/unitorder/scanqrpay',
        'queryPay'      => 'https://vsp.allinpay.com/apiweb/unitorder/query',
        'orderCancel'   => 'https://vsp.allinpay.com/apiweb/unitorder/cancel',
        'orderRefund'   => 'https://vsp.allinpay.com/apiweb/unitorder/refund',
        'addTerm'       => 'https://vsp.allinpay.com/cusapi/merchantapi/addterm',
        'queryTerm'     => 'https://vsp.allinpay.com/cusapi/merchantapi/qryterm',
        'reportPay'     => 'https://service.allinpay.com/customs/pvcapply',
        'queryReport'   => 'https://service.allinpay.com/customs/access'
    ];
    public function __construct( array $apiBody = [] )
    {
        $this->config = $apiBody;
    }
    
   /**
     * 发起支付
     * @param array $body
     */
    public function scanPayOrder( array $body = [])
    {
//        if(empty($this->config['termno'])||empty($this->config['deviceip'])){
//            return Normal::returnCode(Normal::FAIL,'请配置终端号和服务器ip');
//        }
        $terminfo       =[
            'termno'=> $body['termno'],
            'devicetype'=>'11',
            'deviceip'=> $body['deviceip']
        ];
        
        $total_fee      = bcmul((string)$body['total_fee'], '100');
        $getNonceStr    = getNonceStr();
        $request        = [
            'orgid'     => $this->config['orgid']??'',//集团商户号
            'cusid'     => $this->config['cusid'],//商户号
            'appid'     => $this->config['appid'],//应用ID
            'version'   => 11,
            'randomstr' => $getNonceStr,//随机字符串
            'trxamt'    => $total_fee,//交易金额 分
            'reqsn'     => $body['reqsn'],//商户交易单号
            'body'      => $body['body']??'',//订单标题
            'remark'    => $body['remark']??'',
            'authcode'  => $body['authcode'],//支付授权码
            'terminfo'  => json_encode($terminfo),
        ];
        return $this->execute('scanPay',$request);
    }
    
    /**
     * 退款
     * @param array $body
     * @return type
     */
    public function orderRefund( array $body = [])
    {
        $total_fee      = bcmul((string)$body['refund_fee'], '100');
        $getNonceStr    = getNonceStr();
        $request        = [
            'orgid'     => $this->config['orgid']??'',//集团商户号
            'cusid'     => $this->config['cusid'],//商户号
            'appid'     => $this->config['appid'],//应用ID
            'version'   => 11,
            'randomstr' => $getNonceStr,//随机字符串
            'trxamt'    => $total_fee,//交易金额 分
            'reqsn'     => $body['reqsn'],//商户交易单号
            'oldreqsn'  => $body['oldreqsn'],//
            'remark'    => $body['remark']??'',
        ];
        return $this->execute('orderRefund',$request);
    }
    
    /**
     * 交易查询
     * @param array $body
     * @return type
     */
    public function queryPay( array $body){
        $getNonceStr    = getNonceStr();
        $request        = [
            'orgid'     => $this->config['orgid']??'',//集团商户号
            'cusid'     => $this->config['cusid'],//商户号
            'appid'     => $this->config['appid'],//应用ID
            'version'   => 11,
            'randomstr' => $getNonceStr,//随机字符串
            'reqsn'     => $body['reqsn'],//商户交易单号
        ];
        return $this->execute('queryPay',$request);
    }
    
    /**
     * 新增终端，必须
     * @param array $body
     * @return type
     */
    public function addTerm(array $body) {
        $request        = [
            'orgid'     => $this->config['orgid']??'',//集团商户号
            'cusid'     => $this->config['cusid'],//商户号
            'appid'     => $this->config['appid'],//应用ID
            'version'   => 12,
            'termno'     => $body['termno'],//商户交易单号
            'devicetype'=>$body['devicetype']??'11',
            'termsn'    =>$body['termsn']??'',//终端序列号
            'operation' =>$body['operation']??'00',//本次操作标识，取值范围：00：新增；01：修改；02：注销；（注销时，仅需上送termno字段）
            'termstate' =>'00',//取值范围：00：启用；01：注销；注：终端注销时非必填
            'termaddress'=>$body['termaddress'],//省-市-区-详细地址，详细地址长度控制在 30 个汉字以内注：取值范围可参考《省市区结构说明》注：终端注销时非必填
        ];
        return $this->execute('addTerm',$request);
    }
    
     /**
     * 注销终端，必须
     * @param array $body
     * @return type
     */
    public function delTerm(array $body) {
        $request        = [
            'orgid'     => $this->config['orgid']??'',//集团商户号
            'cusid'     => $this->config['cusid'],//商户号
            'appid'     => $this->config['appid'],//应用ID
            'version'   => 12,
            'termno'     => $body['termno'],//商户交易单号
            'devicetype'=>$body['devicetype']??'11',
            'operation' =>'02',//本次操作标识，取值范围：00：新增；01：修改；02：注销；（注销时，仅需上送termno字段）
        ];
        return $this->execute('addTerm',$request);
    }
    
    /**
     * 查询终端
     * @param array $body
     * @return type
     */
    public function queryTerm(array $body) {
        $request        = [
            'orgid'     => $this->config['orgid']??'',//集团商户号
            'cusid'     => $this->config['cusid'],//商户号
            'appid'     => $this->config['appid'],//应用ID
            'version'   => 12,
            'termno'     => $body['termno'],//商户交易单号
            'querytype' =>'UAT',//空或者AT时仅返回AT报备状态   UAT的时候返回AT加银联报备状态
        ];
        return $this->execute('queryTerm',$request);
    }
    
    public function reportPay(array $param) {
        $data=[];
        $data['HEAD']=[
            'VERSION'=>'v5.6',
            'VISITOR_ID'=>'MCT',
            'MCHT_ID'=>$this->config['mch_id'],
            'ORDER_NO'=>getNonceStr(),
            'TRANS_DATETIME'=> date('YmdHis'),
            'CHARSET'=>'1',
            'SIGN_TYPE'=>'1',
            'SIGN_MSG'=>''
        ];
        $data['BODY']=[
            'CUSTOMS_CODE'=> $this->config['customs_code']??'HG014',//海关类别
            'PAYMENT_CHANNEL'=>2,//支付渠道
            'CUS_ID'=>$this->config['cusid'],//验证报关商户号$this->config['cusid']
            'PAYMENT_DATETIME'=>$param['PAYMENT_DATETIME']??date('YmdHis'),//支付时间yyyyMMddHH24mmss
            'MCHT_ORDER_NO'=>$param['MCHT_ORDER_NO'],//商户订单号
            'PAYMENT_ORDER_NO'=>$param['PAYMENT_ORDER_NO'],//支付流水号
            'PAYMENT_AMOUNT'=>bcmul((string)$param['PAYMENT_AMOUNT'], '100'),//支付总额,
            'CURRENCY'=>'156',
            'ESHOP_ENT_CODE'=>$param['ESHOP_ENT_CODE'],//电商平台代码
            'ESHOP_ENT_NAME'=>$param['ESHOP_ENT_NAME'],//电商平台名称
            'PAYER_NAME'=>$param['PAYER_NAME'],//支付人姓名
            'PAPER_TYPE'=>'01',//支付人证件类型01身份证
            'PAPER_NUMBER'=>$param['PAPER_NUMBER'],//支付人证件号码
            'PAPER_PHONE'=>$param['PAPER_PHONE'],
        ];
        if(isset($param['MAIN_PAYMENT_ORDER_NO'])){
            $data['BODY']['MAIN_PAYMENT_ORDER_NO']=$param['MAIN_PAYMENT_ORDER_NO'];//主支付流水号
        }
        if($data['BODY']['CUSTOMS_CODE']=='HG021'){//杭州必填参数
            $data['BODY']['GOODS_FEE']=bcmul((string)$param['GOODS_FEE'], '100');//商品货款金额
            $data['BODY']['TAX_FEE']=bcmul((string)$param['TAX_FEE'], '100');//税款金额
            $data['BODY']['FREIGHT_FEE']=$param['FREIGHT_FEE'];//运费金额
        }
        if($data['BODY']['CUSTOMS_CODE']=='HG018'){//广州必填参数
            $data['BODY']['BIZ_TYPE_CODE']='CUS';//业务类型//广州：填 CUS
            $data['BODY']['ORG_CODE']= $this->config['org_code'];//检验检疫机构代码
            $data['BODY']['IS_CHECK']='F';//是否报南沙国检系统 都填 F ，不报南沙国检
            $data['BODY']['CUSTOMS_AREA_CODE']= $this->config['customs_area_code'];//主管海关代码  根据商户实际情况填写（参见海关提供的关区代码）
        }
        $xml='<BODY>'.$this->arr_to_xml($data['BODY'],false).'</BODY>';            
        $sign=md5($xml.'<key>1234567890</key>'); 
        $data['HEAD']['SIGN_MSG']=strtoupper($sign);     
        $msg='<PAYMENT_INFO>'.$this->arr_to_xml($data,false).'</PAYMENT_INFO>';   
        $parameterValue=base64_encode($msg);
        return $this->executeReport('reportPay', $parameterValue);
    }

    

    /**
     * 获取签名参数
     * @param array $param
     */
    protected function getSign( $param = [] )
    {
        $parameter=$param;
        $parameter['signtype']='RSA';
        ksort($parameter);
        //组装签名字符串
        $bufSignSrc = self::ToUrlParams($parameter);
        //进行RSA签名
        $private_key = chunk_split($this->config['private_key'] , 64, "\n");
        $key = "-----BEGIN RSA PRIVATE KEY-----\n".wordwrap($private_key)."-----END RSA PRIVATE KEY-----";
        $signRes = openssl_sign($bufSignSrc, $signature, $key );
        if( !$signRes ) {
            return Normal::returnCode(Normal::FAIL,"签名失败");
        }
        $parameter['sign'] = base64_encode($signature);
        return Normal::returnCode(Normal::SUC,"签名成功",$parameter);
    }
    
    /**
     * 处理参数签名
     * @param array $array
     * @return string
     */
    public static function ToUrlParams(array $array)
    {
        $buff = "";
        foreach ($array as $k => $v)
        {
            if($v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
    
        $buff = trim($buff, "&");
        return $buff;
    }
    
    public function arr_to_xml($data, $root = true){
        $str = "";
        if ($root) {
            $str .= "<xml>";
        }
        foreach ($data as $key => $val) {
            $key= trim($key);
            
            if (is_array($val)) {
                $child = $this->arr_to_xml($val,false);
                $str.="<$key>$child</$key>";
            } else {
                $val= trim((string)$val);
                $str.="<$key>$val</$key>";
            }
        }
        if ($root) {
            $str .= "</xml>";
        }
        return str_replace(PHP_EOL,"",$str);
    } 
    
    public function xmlToArray($xml)
    {

        libxml_disable_entity_loader(true);
        $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $values;
    }
    
    /**
     * 推送接口
     * @param string $method
     * @param array $body
     */
    public function execute(string $method, array $data)
    {
        if( !isset($this->router[$method]) ) {
            return Normal::returnCode(Normal::FAIL,"当前方式路由不存在【{$method}】");
        }
        
        $url = $this->router[$method];
        $result_sign = $this->getSign($data);
        if( $result_sign['code'] != Normal::SUC ) {
            return $result_sign;
        }
        $data = $result_sign['data'];
        $parameter = [
            [
                'tag'       => 'allinpay_'.$method,
                'url'       => $url,
                'data'      => $data,
                'method'    => 'post',
                'format'    => 'x-www-form-urlencoded',
                'header'    => [],
            ],
        ];
        $resTask    = TaskWorker::instance()->addTask($parameter);
        $resTask    = current($resTask);
        $response   = json_decode($resTask['response']['result'],true);
        $returnMsg  = [];
        if( isset($response['retcode']) ) 
        {
            if(in_array($method, ['addTerm','queryTerm'])){
                if( $response['retcode'] == 'SUCCESS' ) 
                {
                    $returnMsg = Normal::returnCode(Normal::SUC,"交易成功",$response);
                } 
                else 
                {
                    $errmsg=$response['errmsg']??$response['retmsg'];
                    $returnMsg = Normal::returnCode(Normal::FAIL,$errmsg,$response);
                }
            }else{
                if( $response['retcode'] == 'SUCCESS' && ($response['trxstatus'] == '0000'||$response['trxstatus'] == '2000'||$response['trxstatus'] == '2008' )) 
                {
                    $returnMsg = Normal::returnCode(Normal::SUC,"交易成功",$response);
                } 
                else 
                {
                    $errmsg=$response['errmsg']??$response['retmsg'];
                    $returnMsg = Normal::returnCode(Normal::FAIL,$errmsg,$response);
                }
            }
        } else {
            $returnMsg = Normal::returnCode(Normal::FAIL,"请求失败");
        }
        return $returnMsg;
    }
    
    /**
     * 申报处理
     * @param string $method
     * @param array $data
     * @return type
     */
     public function executeReport(string $method,  $data)
    {
        if( !isset($this->router[$method]) ) {
            return Normal::returnCode(Normal::FAIL,"当前方式路由不存在【{$method}】");
        }
        
        $url = $this->router[$method];
        $parameter = [
            [
                'tag'       => 'allinpay_'.$method,
                'url'       => $url,
                'data'      => ['data'=>$data],
                'method'    => 'post',
                'format'    => 'x-www-form-urlencoded',
                'header'    => [],
            ],
        ];
        $resTask    = TaskWorker::instance()->addTask($parameter);
        $resTask    = current($resTask);
        $response   = $this->xmlToArray(base64_decode($resTask['response']['result']));        
        $returnMsg  = [];
        if( isset($response['BODY']['RETURN_CODE']) ) 
        {
            if( $response['BODY']['RETURN_CODE'] == '0000' ) 
            {
                $returnMsg = Normal::returnCode(Normal::SUC,"交易成功",$response);
            } 
            else 
            {
                $returnMsg = Normal::returnCode(Normal::FAIL,$response['BODY']['RETURN_MSG'],$response);
            }
        } else {
            $returnMsg = Normal::returnCode(Normal::FAIL,"请求失败");
        }
        return $returnMsg;
    }
}
